#include "globals.h"

PCBStr *sem_queue_process_remove(int sem_no);

typedef struct semaphore_type {
	int being_used;
	char owner[name_size];
	int protected_memory;
	//blocked queue
    PCBStr *head;
    PCBStr *tail;
    int size;

} semaphore;

semaphore *sem;

void setup_semaphores()
{
	sem = (semaphore*)malloc(sizeof(semaphore) * num_sems);
	int i;
	for(i = 0; i < num_sems; i++)
	{
		sem[i].head = malloc(sizeof(PCBStr));
		sem[i].tail = malloc(sizeof(PCBStr));
		sem[i].size = 0;
		sem[i].being_used = 0;
	}
}

void add_pcb_sem_blocked_queue(PCBStr *pcb, int sem_no)
{
 pcb->next =  sem[sem_no].tail;
 pcb->prev =  sem[sem_no].tail->prev;
 sem[sem_no].tail->prev->next = pcb;
 sem[sem_no].tail->prev =  pcb;
 sem[sem_no].size++;
 printf("Process %s added to blocked queue of Semaphor %d.\n", pcb->pid->name, sem_no);
 }

/**
*  Returns 0 if successful, -1 if not.
**/
int sem_down(PCBStr *pcb,int sem_no) {
	if (sem[sem_no].being_used == 0) {
		sem[sem_no].being_used = 1;
		strcpy(sem[sem_no].owner, pcb->pid->name);
		printf("Process %s is owner of semaphor %d.\n", pcb->pid->name, sem_no);
		return 0;
	} else {
		//add calling process to queue for sems[sem_no]
		add_pcb_sem_blocked_queue(pcb, sem_no);
		printf("Process %s is now added to blocked queue of semaphor %d.\n", pcb->pid->name, sem_no);
		return -1;
	}
}

/**
*  Returns 0 if successful, -1 if not.
**/
int sem_up(int sem_no, PCBStr *pcb) {
	if (sem[sem_no].owner == pcb->pid->name &&  sem[sem_no].being_used == 1) {
		sem[sem_no].being_used = 0;
		PCBStr *skipped = sem_queue_process_remove(sem_no);
		strcpy(sem[sem_no].owner , skipped->pid->name);
		printf("Process %s successfully made a sem_up call on semaphor %d.\n", pcb->pid->name, sem_no);
		return 0;
	} else {
		//ignore call
		printf("Process %s's sem_up call on semaphor %d, was unsuccessful.\n", pcb->pid->name, sem_no);
		return -1;
	}
}


/**
* Returns the pcb of the process removed from the queue.
*
**/
PCBStr *sem_queue_process_remove(int sem_no) {
        PCBStr *skipped;
        if (sem[sem_no].size == 0) {
                printf("Semaphore %d queue provided is NULL or empty, exiting...\n", sem_no);
                return NULL;
        }

        else
        {
                skipped = sem[sem_no].head->next;
                sem[sem_no].head->next = skipped->next;
                sem[sem_no].head->next->prev = sem[sem_no].head;
                skipped->next->prev = skipped->prev;
                sem[sem_no].size--;
                printf("Process %s removed from front of the semaphore %d blocked queue.\n", skipped->pid->name, sem_no);

        }
        return skipped;
}

/**
*  Is able to write to shared memory only , if it has is the owner of semaphor.
*  Returns 0 if successful, -1 if not.
**/
int write_to_protected_mem(int sem_no, PCBStr *pcb, int value)
{

	if (sem[sem_no].owner == pcb->pid->name) {
		sem[sem_no].protected_memory = value;
		printf("Process %s successfully wrote to protected memory of semaphor %d. New value is %d \n", pcb->pid->name, sem_no, value);
		return 0;
	} else {
		//ignore call
		printf("Process %s was not able to write to protected memory of semaphor %d.\n", pcb->pid->name, sem_no);
		return -1;
	}
}

/**
*  Is able to read from shared memory only , if it has is the owner of semaphor.
*  Returns value if successful, -1 if not.
**/
int read_from_protected_mem(int sem_no, PCBStr *pcb)
{

	if (sem[sem_no].owner == pcb->pid->name) {
		printf("Process %s successfully read protected memory of semaphor %d. Value is %d \n", pcb->pid->name, sem_no, sem[sem_no].protected_memory);
		return sem[sem_no].protected_memory;
	} else {
		//ignore call
		printf("Process %s was not able to read from protected memory of semaphor %d.\n", pcb->pid->name, sem_no);

		return -1;
	}
}


